//
// Created by yanhai on 2021/11/5.
//

#include <utility/util/defer.h>
#include "hash_table_thread.h"

HashTableThread::node_type HashTableThread::Find(HashTableThread::value_type v)
{
    node_type f;

    /* get our hash bucket and lock it */
    const unsigned int hash = v->Hash();
    HashTableThreadBucket *fb = &array[hash % array_size];

    std::lock_guard<std::mutex> lock(fb->mutex);

//    printf("fb %p fb->head %p\n", fb, fb->head);

    /* see if the bucket already has a flow */
    if (fb->head == nullptr) {
        f = NewNode();
        if (f == nullptr) {
            return nullptr;
        }

        /* flow is locked */
        fb->head = f;

        /* got one, now lock, initialize and return */
        f->InitByValue(v);
        return f;
    }

    f = fb->head;
    do {
        node_type next_f = nullptr;
        if (f->CompareValue(v)) {
            f->mutex.lock();
            return f;
        }

        next_f = f->hnext;

        if (next_f == nullptr) {
            f = NewNode();
            if (f == nullptr) {
                return nullptr;
            }

            /* flow is locked */
            f->hnext = fb->head;
            fb->head = f;

            /* initialize and return */
            f->InitByValue(v);
            return f;
        }
        f = next_f;
    } while (f != nullptr);

    /* should be unreachable */
    return nullptr;
}

bool HashTableThread::CheckTimeout(std::time_t now,
                                   const std::function<bool(node_type, std::time_t)> &IsTimeout,
                                   const std::function<void(node_type)> &CallBack)
{
    for (unsigned int idx = 0; idx < array_size; idx++) {
        HashTableThreadBucket *fb = &array[idx];

        if (!fb->mutex.try_lock()) {
            continue;
        }

        defer bucketUnlock([fb]() { fb->mutex.unlock(); });

        node_type prev_f = nullptr;
        node_type f = fb->head;
        while (f != nullptr) {
            node_type next_f = f->hnext;

            if (!f->mutex.try_lock()) {
                prev_f = f;
                f = next_f;
                continue;
            }

            if (IsTimeout(f, now)) {
                CallBack(f);

                /* remove from the hash */
                if (prev_f != nullptr) {
                    prev_f->hnext = f->hnext;
                } else {
                    fb->head = f->hnext;
                }
                f->hnext = nullptr;

                // free
                f->mutex.unlock();
                FreeNode(f);

                f = next_f;
                continue;
            }

            f->mutex.unlock();

            prev_f = f;
            f = next_f;
        }
    }

    return true;
}
